﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;

namespace ChangeNamespace
{
	class Program
	{
		private static Regex search;
		private static string replace;
		private static string path;

		private static HashSet<string> excludedDirectories = new HashSet<string>() { "bin", "obj", };
		private static HashSet<string> includedFilenames = new HashSet<string>() { ".sln", ".csproj", ".cs", };

		static void Main(string[] args)
		{
			var errors = ParseCommandLine(args);
			if (errors != null)
			{
				PrintHelp(errors);

				return;
			}

			ChangeNamespace(path);
		}

		private static List<string> ParseCommandLine(string[] args)
		{
			var errors = new List<string>();

			if (args == null || args.Length == 0)
			{
				return errors;
			}

			for (int i = 0; i < args.Length; ++i)
			{
				switch (args[i])
				{
				default:
					errors.Add("Invalid Parameter: " + args[i]);
					break;

				case "-Namespace":
					search = new Regex(args[++i]);
					replace = args[++i];
					break;

				case "-Path":
					path = args[++i];
					break;
				}
			}

			if (search == null || replace == null)
			{
				errors.Add("Missing -Namespace");
			}

			if (path == null)
			{
				path = Environment.CurrentDirectory;
			}

			if (errors.Count == 0)
			{
				return null;
			}
			else
			{
				return errors;
			}
		}

		private static void PrintHelp(List<string> errors)
		{
			Console.WriteLine();
			Console.WriteLine("ChangeNamespace.exe");
			Console.WriteLine(" -Namespace regex replace");
			Console.WriteLine(" -Path path");
			Console.WriteLine();

			if (errors != null && errors.Count > 0)
			{
				foreach (var error in errors)
				{
					Console.WriteLine(error);
				}

				Console.WriteLine();
			}
		}

		private static void ChangeNamespace(string path)
		{
			ChangeDirectory(path);
			ChangeFiles(path);
		}

		private static void ChangeDirectory(string path)
		{
			var directories = Directory.GetDirectories(path);
			foreach (var directory in directories)
			{
				var name = Path.GetFileName(directory);
				if (excludedDirectories.Contains(name))
				{
					continue;
				}

				string destination;
				var replaced = search.Replace(name, replace);
				if (replaced != name)
				{
					var source = Path.GetDirectoryName(directory);
					destination = Path.Combine(source, replaced);
					Directory.Move(directory, destination);
				}
				else
				{
					destination = directory;
				}

				ChangeNamespace(destination);
			}
		}

		private static void ChangeFiles(string path)
		{
			var filenames = Directory.GetFiles(path);
			foreach (var filename in filenames)
			{
				var destination = ChangeFilePath(filename);
				var extention = Path.GetExtension(destination);
				if (includedFilenames.Contains(extention))
				{
					ChangeFileContents(destination);
				}
			}
		}

		private static string ChangeFilePath(string filename)
		{
			var name = Path.GetFileName(filename);
			var replaced = search.Replace(name, replace);
			if (replaced != name)
			{
				var source = Path.GetDirectoryName(filename);
				var destination = Path.Combine(source, replaced);
				File.Move(filename, destination);

				return destination;
			}
			else
			{
				return filename;
			}
		}

		private static void ChangeFileContents(string path)
		{
			var lines = File.ReadAllLines(path);
			bool changed = false;
			for (int i = lines.Length - 1; i >= 0; --i)
			{
				var line = lines[i];
				var replaced = search.Replace(line, replace);
				if (replaced != line)
				{
					changed = true;
					lines[i] = replaced;
				}
			}

			if (changed)
			{
				File.WriteAllLines(path, lines);
			}
		}
	}
}
